export default class Tree {
  static getValue(obj: any, keyName: string) {
    return keyName.indexOf('.') > 1
      ? obj[keyName.split('.')[0]][keyName.split('.')[1]]
      : obj[keyName];
  }

  /**
   * 从数组构建树
   * @param {*} data 原始数据列表
   * @param {*} keyName 关键字字段名称， 默认为'id'
   * @param {*} parentKeyName 父关键字字段名称，默认为'pId'
   */
  static toTree(
    data: Array<any>,
    keyName = 'id',
    parentKeyName = 'pId',
    nameField = 'name',
    parentNameField = 'parentName',
    childrenFieldName = 'children'
  ) {
    data = JSON.parse(JSON.stringify(data));
    // 没有父节点的数据
    const parents = data.filter(
      (value) =>
        Tree.getValue(value, parentKeyName) === 'undefined' ||
        Tree.getValue(value, parentKeyName) === null ||
        Tree.getValue(value, parentKeyName) === '' ||
        Tree.getValue(value, parentKeyName) === 'string' ||
        Tree.getValue(value, parentKeyName) === '-1'
    );
    // 有父节点的数据
    const children = data.filter(
      (value) =>
        (Tree.getValue(value, parentKeyName) !== 'undefined' &&
          Tree.getValue(value, parentKeyName) != null &&
          Tree.getValue(value, parentKeyName) !== 'string') ||
        Tree.getValue(value, parentKeyName) !== ''
    );
    // 定义转换方法的具体实现
    // eslint-disable-next-line no-shadow
    const translator = (parents: Array<any>, children: Array<any>) => {
      // 遍历父节点数据
      parents.forEach((parent) => {
        // 遍历子节点数据
        children.forEach((current, index) => {
          // 此时找到父节点对应的一个子节点
          if (
            Tree.getValue(current, parentKeyName) ===
              Tree.getValue(parent, keyName) &&
            Tree.getValue(current, keyName) !== Tree.getValue(parent, keyName)
          ) {
            current[parentNameField] = parent[nameField];
            // 对子节点数据进行深复制，这里只支持部分类型的数据深复制，对深复制不了解的童靴可以先去了解下深复制
            try {
              const temp = JSON.parse(JSON.stringify(children));
              // 让当前子节点从temp中移除，temp作为新的子节点数据，这里是为了让递归时，子节点的遍历次数更少，如果父子关系的层级越多，越有利
              temp.splice(index, 1);
              // 让当前子节点作为唯一的父节点，去递归查找其对应的子节点
              translator([current], temp);
              // 把找到子节点放入父节点的children属性中
              // eslint-disable-next-line no-unused-expressions
              typeof parent[childrenFieldName] !== 'undefined' &&
              parent[childrenFieldName] !== null
                ? parent[childrenFieldName].push(current)
                : (parent[childrenFieldName] = [current]);
            } catch (e) {
              console.log(e);
            }
          }
        });
      });
    };
    // 调用转换方法
    translator(parents, children);
    // 返回最终的结果
    return parents;
  }

  /**
   * 获取树结构数据树深度
   * @param {*} data 树结构数据
   * @param {*} childrenKeyName 子数组关键字字段，默认为'children'
   */
  // @ts-ignore
  static getTreeDeepth(data = [], childrenKeyName = 'children') {
    if (!data || (data && data.length === 0)) {
      return 0;
    }
    // @ts-ignore
    const maxChildrenDepth = data.map((v) =>
      Tree.getTreeDeepth(v[childrenKeyName], childrenKeyName)
    );
    return 1 + Math.max(...maxChildrenDepth);
  }

  /**
   * 过滤树结构数据
   * @param {*} data 树结构数组
   * @param {*} predicate 过滤方法，返回true/false
   * @param {*} predicate 过滤方法，返回true/false
   * @param {*} childKey 子数组关键字字段，默认为'children'
   */
  static filterTree(nodes: Array<any>, predicate: any, childKey = 'children') {
    if (!nodes || !nodes.length) return [];
    const children = [];
    // eslint-disable-next-line no-restricted-syntax
    for (let node of nodes) {
      node = { ...node };
      const sub = Tree.filterTree(node[childKey], predicate, childKey);
      if ((sub && sub.length) || predicate(node)) {
        // eslint-disable-next-line no-unused-expressions
        sub && (node[childKey] = sub);
        children.push(node);
      }
    }
    return children.length ? children : [];
  }

  /**
   * 寻找子节点
   * @param {*} id id关键字
   * @param {*} treeData 树数据
   * @param {*} childKey 子数组关键字字段，默认为'children'
   */
  static findInTree(
    id: string,
    treeData: Array<any>,
    childKey = 'children',
    fieldKey = 'id'
  ) {
    if (!treeData || !treeData.length) return false;
    // eslint-disable-next-line no-use-before-define
    const treeMapArray = Tree.getTreeMap(treeData, childKey);
    // @ts-ignore
    return treeMapArray.find((item) => item[fieldKey] === id);
  }

  // tree 铺平方法
  static getNodeMap = (node: any, parentNode: any, childKey = 'children') => {
    node.parentNode = parentNode;
    const nodeMap = [node];
    // @ts-ignore
    if (node[childKey] && node[childKey].length) {
      // @ts-ignore
      node[childKey].forEach((item) => nodeMap.push(...getNodeMap(item, node)));
    }
    return nodeMap;
  };

  static getTreeMap(tree: Array<any>, childKey = 'children') {
    if (!(tree instanceof Array)) return;
    let treeMap: Array<any>;
    tree.forEach((node) => {
      treeMap.push(...Tree.getNodeMap(node, tree, childKey));
    });
    // @ts-ignore
    // eslint-disable-next-line consistent-return
    return treeMap;
  }

  // 铺平树(在用)
  // @ts-ignore
  static flatTreeData(tree: any, childKey = 'children') {
    let result = [];
    // eslint-disable-next-line no-restricted-syntax
    for (const item of tree) {
      const res = JSON.parse(JSON.stringify(item)); // 先克隆一份数据作为第一层级的填充
      delete res[childKey];
      result.push(res);
      if (item[childKey] instanceof Array && item[childKey].length > 0) {
        // 如果当前child为数组并且长度大于0，才可进入flag()方法
        result = result.concat(Tree.flatTreeData(item[childKey]));
      }
    }
    return result;
  }

  /**
   * 移除子树
   * @param {*} id id关键字
   * @param {*} treeData 树数据
   * @param {*} childKey 子数组关键字字段，默认为'children'
   */
  static removeSubTree(
    id: string,
    treeData: Array<any>,
    childKey = 'children',
    fieldKey = 'id',
    parentFieldName = 'parentId'
  ) {
    const node = Tree.findInTree(id, treeData, childKey, fieldKey);
    if (node) {
      const parent = Tree.findInTree(
        node[parentFieldName],
        treeData,
        childKey,
        fieldKey
      );
      if (parent && parent[childKey] && parent[childKey].length) {
        const index = parent[childKey].findIndex(
          (item: any) => item[fieldKey] === id
        );
        parent[childKey].splice(index, 1);
      } else {
        const index = treeData.findIndex((item) => item[fieldKey] === id);
        treeData.splice(index, 1);
      }
    }
  }
}
